
using System;
using System.IO;
using System.Runtime.Serialization;
using com.calitha.goldparser.lalr;
using com.calitha.commons;

namespace com.calitha.goldparser
{

    [Serializable()]
    public class SymbolException : System.Exception
    {
        public SymbolException(string message) : base(message)
        {
        }

        public SymbolException(string message,
            Exception inner) : base(message, inner)
        {
        }

        protected SymbolException(SerializationInfo info,
            StreamingContext context) : base(info, context)
        {
        }

    }

    [Serializable()]
    public class RuleException : System.Exception
    {

        public RuleException(string message) : base(message)
        {
        }

        public RuleException(string message,
                             Exception inner) : base(message, inner)
        {
        }

        protected RuleException(SerializationInfo info,
                                StreamingContext context) : base(info, context)
        {
        }

    }

    enum SymbolConstants : int
    {
        SYMBOL_EOF              = 0 , // (EOF)
        SYMBOL_ERROR            = 1 , // (Error)
        SYMBOL_WHITESPACE       = 2 , // (Whitespace)
        SYMBOL_DOLLER           = 3 , // Doller
        SYMBOL_EQUALTO          = 4 , // EqualTo
        SYMBOL_GREATERTHAN      = 5 , // GreaterThan
        SYMBOL_IDENTIFIER       = 6 , // Identifier
        SYMBOL_LESSTHAN         = 7 , // LessThan
        SYMBOL_SEMICOLON        = 8 , // SemiColon
        SYMBOL_STRINGLITERAL    = 9 , // StringLiteral
        SYMBOL_CAPTURENAME      = 10, // <CaptureName>
        SYMBOL_PATTERN          = 11, // <Pattern>
        SYMBOL_PATTERNATOM      = 12, // <PatternAtom>
        SYMBOL_PATTERNNAME      = 13, // <PatternName>
        SYMBOL_PATTERNREFERENCE = 14, // <PatternReference>
        SYMBOL_PROGRAM          = 15, // <Program>
        SYMBOL_REGEXSTRING      = 16, // <RegexString>
        SYMBOL_STATEMENT        = 17, // <Statement>
        SYMBOL_STATEMENTS       = 18, // <Statements>
        SYMBOL_WORD             = 19  // <Word>
    };

    enum RuleConstants : int
    {
        RULE_PROGRAM                                                 = 0 , // <Program> ::= <Statements>
        RULE_STATEMENTS                                              = 1 , // <Statements> ::= 
        RULE_STATEMENTS_SEMICOLON                                    = 2 , // <Statements> ::= <Statement> SemiColon <Statements>
        RULE_STATEMENT_EQUALTO                                       = 3 , // <Statement> ::= <PatternName> EqualTo <Pattern>
        RULE_PATTERNNAME_IDENTIFIER                                  = 4 , // <PatternName> ::= Identifier
        RULE_PATTERN                                                 = 5 , // <Pattern> ::= <PatternAtom>
        RULE_PATTERN2                                                = 6 , // <Pattern> ::= <PatternAtom> <Pattern>
        RULE_PATTERNATOM                                             = 7 , // <PatternAtom> ::= <Word>
        RULE_PATTERNATOM2                                            = 8 , // <PatternAtom> ::= <RegexString>
        RULE_PATTERNATOM3                                            = 9 , // <PatternAtom> ::= <PatternReference>
        RULE_WORD_IDENTIFIER                                         = 10, // <Word> ::= Identifier
        RULE_REGEXSTRING_STRINGLITERAL                               = 11, // <RegexString> ::= StringLiteral
        RULE_PATTERNREFERENCE_DOLLER_IDENTIFIER_LESSTHAN_GREATERTHAN = 12, // <PatternReference> ::= Doller Identifier LessThan <CaptureName> GreaterThan
        RULE_CAPTURENAME_IDENTIFIER                                  = 13  // <CaptureName> ::= Identifier
    };

        // this class will construct a parser without having to process
        //  the CGT tables with each creation.  It must be initialized
        //  before you can call CreateParser()
        public sealed class MyParserFactory
        {
            static bool _init = false;
            
                private MyParserFactory()
                {
                }
                
                public static void InitializeFactory(System.IO.Stream CGTStream)
                {
                        if (!_init)
                        {
                           MyParser dummyParser = new MyParser(CGTStream);
                           _init = true;
                        }
                }
                
                public static MyParser CreateParser()
                {
                   if (_init)
                   {
                                return new MyParser();
                   }
                   throw new Exception("You must first call InitiailzeFactory before creating a parser!");
                }
        }
        
    public class MyParser
    {
        
            // static because we only ever need to load it once
            static CGTReader CGTRdr = null;
                
internal MyParser()
        {
            Init(null);
        }

                
        private LALRParser parser;

        public MyParser(string filename)
        {
            if (CGTRdr == null)
                {
                   FileStream stream = new FileStream(filename,
                                       FileMode.Open, 
                                       FileAccess.Read, 
                                       FileShare.Read);
                   Init(stream);
                   stream.Close();
                }
            else
                {
                    Init(null); 
                }
                        
        }

        public MyParser(string baseName, string resourceName)
        {
            byte[] buffer = ResourceUtil.GetByteArrayResource(
                System.Reflection.Assembly.GetExecutingAssembly(),
                baseName,
                resourceName);
            MemoryStream stream = new MemoryStream(buffer);
            Init(stream);
            stream.Close();
        }

        public MyParser(Stream stream)
        {
            Init(stream);
        }

        private void Init(Stream stream)
        {
                    if (CGTRdr == null)
                        {
                            CGTRdr = new CGTReader(stream);
                        }
                        
            parser = GCTRdr.CreateNewParser();
                        
            parser.TrimReductions = false;
            parser.StoreTokens = LALRParser.StoreTokensMode.NoUserObject;

            parser.OnTokenError += new LALRParser.TokenErrorHandler(TokenErrorEvent);
            parser.OnParseError += new LALRParser.ParseErrorHandler(ParseErrorEvent);
        }

        public void Parse(string source)
        {
            NonterminalToken token = parser.Parse(source);
            if (token != null)
            {
                Object obj = CreateObject(token);
                //todo: Use your object any way you like
            }
        }

        private Object CreateObject(Token token)
        {
            if (token is TerminalToken)
                return CreateObjectFromTerminal((TerminalToken)token);
            else
                return CreateObjectFromNonterminal((NonterminalToken)token);
        }

        private Object CreateObjectFromTerminal(TerminalToken token)
        {
            switch (token.Symbol.Id)
            {
                case (int)SymbolConstants.SYMBOL_EOF :
                //(EOF)
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_ERROR :
                //(Error)
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_WHITESPACE :
                //(Whitespace)
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_DOLLER :
                //Doller
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_EQUALTO :
                //EqualTo
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_GREATERTHAN :
                //GreaterThan
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_IDENTIFIER :
                //Identifier
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_LESSTHAN :
                //LessThan
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_SEMICOLON :
                //SemiColon
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_STRINGLITERAL :
                //StringLiteral
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_CAPTURENAME :
                //<CaptureName>
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_PATTERN :
                //<Pattern>
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_PATTERNATOM :
                //<PatternAtom>
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_PATTERNNAME :
                //<PatternName>
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_PATTERNREFERENCE :
                //<PatternReference>
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_PROGRAM :
                //<Program>
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_REGEXSTRING :
                //<RegexString>
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_STATEMENT :
                //<Statement>
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_STATEMENTS :
                //<Statements>
                //todo: Create a new object that corresponds to the symbol
                return null;

                case (int)SymbolConstants.SYMBOL_WORD :
                //<Word>
                //todo: Create a new object that corresponds to the symbol
                return null;

            }
            throw new SymbolException("Unknown symbol");
        }

        public Object CreateObjectFromNonterminal(NonterminalToken token)
        {
            switch (token.Rule.Id)
            {
                case (int)RuleConstants.RULE_PROGRAM :
                //<Program> ::= <Statements>
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_STATEMENTS :
                //<Statements> ::= 
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_STATEMENTS_SEMICOLON :
                //<Statements> ::= <Statement> SemiColon <Statements>
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_STATEMENT_EQUALTO :
                //<Statement> ::= <PatternName> EqualTo <Pattern>
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_PATTERNNAME_IDENTIFIER :
                //<PatternName> ::= Identifier
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_PATTERN :
                //<Pattern> ::= <PatternAtom>
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_PATTERN2 :
                //<Pattern> ::= <PatternAtom> <Pattern>
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_PATTERNATOM :
                //<PatternAtom> ::= <Word>
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_PATTERNATOM2 :
                //<PatternAtom> ::= <RegexString>
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_PATTERNATOM3 :
                //<PatternAtom> ::= <PatternReference>
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_WORD_IDENTIFIER :
                //<Word> ::= Identifier
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_REGEXSTRING_STRINGLITERAL :
                //<RegexString> ::= StringLiteral
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_PATTERNREFERENCE_DOLLER_IDENTIFIER_LESSTHAN_GREATERTHAN :
                //<PatternReference> ::= Doller Identifier LessThan <CaptureName> GreaterThan
                //todo: Create a new object using the stored tokens.
                return null;

                case (int)RuleConstants.RULE_CAPTURENAME_IDENTIFIER :
                //<CaptureName> ::= Identifier
                //todo: Create a new object using the stored tokens.
                return null;

            }
            throw new RuleException("Unknown rule");
        }

        private void TokenErrorEvent(LALRParser parser, TokenErrorEventArgs args)
        {
            string message = "Token error with input: '"+args.Token.ToString()+"'";
            //todo: Report message to UI?
        }

        private void ParseErrorEvent(LALRParser parser, ParseErrorEventArgs args)
        {
            string message = "Parse error caused by token: '"+args.UnexpectedToken.ToString()+"'";
            //todo: Report message to UI?
        }

    }
}
